using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;

using System.IO;
using Athena.Core;

namespace Athena.IO
{
    public class JarnacWriter : IModelWriter
    {
        public JarnacWriter(Stream oStream)
        {
            _oStream = oStream;
        }

        class NamedValue
        {
            public NamedValue(string sName, string sValue)
            {
                Name = sName;
                Value = sValue;
            }
            string Name;
            string Value;
        }

        public static void InitializeListsFromModel(
            Module oModule,
            List<string> oFloating,
            List<string> oBoundary,
            List<string> oRules,
            List<string> oReactions,
            List<string> oAssignments)
        {
            //oModel.RenameDuplicateIDs();
            oModule.UpdateParameters();

            foreach (Edge e in Module.Empty.AllEdges)
            {
                if (oModule.IsParentOf(e))
                {
                    oBoundary.Add(Module.Empty.ID);
                    break;
                }
            }

            Hashtable nodes = oModule.AllNodes;
            foreach (string id in nodes.Keys)
            {
                Item item = nodes[id] as Item;
                if (item is Species)
                {
                    Species oNode = item as Species;
                    if (oNode.GetBoundary(oModule))
                        oBoundary.Add(id);
                    else
                        oFloating.Add(id);

                    oAssignments.Add("p." + id + " = " + ((Species)oNode).Concentration.ToString());
                }
            }
            string Parameters = oModule.GetParameterInitializations(";");

            if (!string.IsNullOrEmpty(Parameters) && Parameters.Contains("="))
            {
                string[] sAssignments = Parameters.Split(new char[] { ';' } , StringSplitOptions.RemoveEmptyEntries);
                foreach (string s in sAssignments)
                {
                    oAssignments.Add("p." + s);
                }
            }

            if (oModule.GlobalRules != null)
            {
                //string[] sRules = oModule.Rules.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries);

                Hashtable rules = oModule.GlobalRules;

                foreach (string s in rules.Keys)
                {
                    oRules.Add(s + " = " + (string)rules[s] + ";");
                }
            }

            Hashtable parts = oModule.AllParts;

            foreach (string id in parts.Keys)
            {
                Part p = parts[id] as Part;
                if (p != null)
                    oRules.Add(id + " = " + p.PoPS.GetRate(oModule) + ";");
            }

            Hashtable reactions = oModule.AllReactions;
            foreach (string id in reactions.Keys)
            {
                Edge edge = reactions[id] as Edge;

                if (edge != null && edge.IsReaction)
                {
                    oReactions.Add(id + ":" + edge.ToString(oModule,false));
                }
            }

        }
        
        private void WriteFloatingSpecies(List<string> oFloating, StreamWriter oWriter)
        {
            for (int i = 0; i < oFloating.Count; i++)
            {
                if (i == 0)
                    oWriter.Write("\tvar ");

                oWriter.Write(oFloating[i]);

                if (i == oFloating.Count - 1)
                    oWriter.Write(";" + Environment.NewLine);
                else
                    oWriter.Write(", ");

            }
        }
        private void WriteBoundarySpecies (List<string> oBoundary, StreamWriter oWriter)
        {
            for (int i = 0; i < oBoundary.Count; i++)
            {
                if (i == 0)
                    oWriter.Write("\text ");

                oWriter.Write(oBoundary[i]);

                if (i == oBoundary.Count - 1)
                    oWriter.Write(";" + Environment.NewLine);
                else
                    oWriter.Write(", ");
            }
        }
        private void WriteRules(List<string> oRules, StreamWriter oWriter)
        {
            foreach (string s in oRules)
            {
                oWriter.WriteLine("\t" + s);
            }
        }
        private void WriteReactions(List<string> oReactions, StreamWriter oWriter)
        {
            foreach (string s in oReactions)
            {
                oWriter.WriteLine("\t" + s);
            }
        }
        private void WriteAssignments(List<string> oAssignments, StreamWriter oWriter)
        {
            foreach (string s in oAssignments)
            {
                oWriter.WriteLine(s+ ";");
            }
        }

        public override void WriteModel(System.IO.Stream oStream, Model oModel)
        {
            WriteModule(oStream, oModel.BackEndModel);
        }

        public void WriteModule(System.IO.Stream oStream, Module oModel)
        {
            if (oStream == null || !oStream.CanWrite)
                throw new ArgumentException("The given stream is not ready ... ");
            if (oModel == null)
                throw new ArgumentNullException("oModel");

            List<string> oFloating = new List<string>();
            List<string> oBoundary = new List<string>();
            List<string> oRules = new List<string>();
            List<string> oReactions = new List<string>();
            List<string> oAssignments = new List<string>();

            InitializeListsFromModel(oModel, oFloating, oBoundary, oRules, oReactions, oAssignments);

            StreamWriter oWriter = new StreamWriter(oStream);

            oWriter.WriteLine("p = defn " + oModel.ID);
            oWriter.WriteLine();

            WriteFloatingSpecies(oFloating, oWriter);
            WriteBoundarySpecies(oBoundary, oWriter);
            WriteRules(oRules, oWriter);
            WriteReactions(oReactions, oWriter);

            oWriter.WriteLine();
            oWriter.WriteLine("end;");
            oWriter.WriteLine();

            WriteAssignments(oAssignments, oWriter);

            oWriter.Flush();
        }
    }
}
